<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

	//require_once("../Model/LoaderModel.php");

	class Warehouse
	{
		public $CarList = array();
		public function addCar(CarGroup $car)
		{
			$this->CarList[] = $car;
		}
		public function getCar($index)
		{
			return $this->CarList[$index];
		}
        public function clearCarGroup()
        {
            foreach($this->CarList as $key => $val)
                unset($this->CarList[$key]);
        }
		
		public $DeliveryOrderList = array();
		public function addDeliveryOrder(DeliveryOrder $deliveryOrder)
		{
			//$deliveryOrder->ID = sizeof($this->DeliveryOrderList) + 1; 
			$this->DeliveryOrderList[] = $deliveryOrder;
		}
		public function getDeliveryOrder($index)
		{
			return $this->DeliveryOrderList[$index];
		}

        public function getUndeliveredDO()
        {
            $uDO = array();
            foreach($this->DeliveryOrderList as $key => $val)
            {
                if(!$val->Delivered)
                    $uDO[] = $val;
            }
            return $uDO;
        }
        public function clearDeliveryOrder()
        {
            foreach($this->DeliveryOrderList as $key => $val)
                unset($this->DeliveryOrderList[$key]);
        }

        public function reset()
        {
            $this->clearCarGroup();
            $this->clearDeliveryOrder();
        }
	}
	
	class Loader
	{
		private $warehouse = null;
		public function getWarehouse() { return $this->warehouse; }

        private $carGroupTemplate = null;
        private function loadCarGroupTemplate($db)
        {
            $this->carGroupTemplate = array();
            $records = $db->query("SELECT * FROM cargroup")->result();

            for($i=0; $i<sizeof($records); $i++)
            {
                $record = $records[$i];
                $this->carGroupTemplate[] = new CarGroup($record->CarGroupID, $record->MaxDistance, $record->MaxWeight, $record->MaxVolume);
            }

            /*$this->carGroupTemplate[] = new Car("Group 01", 500, 5000, 10000);
            $this->carGroupTemplate[] = new Car("Group 02", 500, 5000, 10000);
            $this->carGroupTemplate[] = new Car("Group 03", 500, 5000, 10000);
            /*$this->carGroupTemplate[] = new Car("Group 04", 500, 5000, 10000);
            $this->carGroupTemplate[] = new Car("Group 05", 500, 5000, 10000);
            $this->carGroupTemplate[] = new Car("Group 06", 500, 5000, 10000);
            $this->carGroupTemplate[] = new Car("Group 07", 500, 5000, 10000);
            $this->carGroupTemplate[] = new Car("Group 08", 500, 5000, 10000);
            $this->carGroupTemplate[] = new Car("Group 09", 500, 5000, 10000);
            $this->carGroupTemplate[] = new Car("Group 10", 500, 5000, 10000);
            $this->carGroupTemplate[] = new Car("Group 10", 500, 5000, 10000);
            $this->carGroupTemplate[] = new Car("Group 10", 500, 5000, 10000);*/
        }

		public function __construct()
		{
			$this->warehouse = new Warehouse();
		}
		
		private function getStarterNode(CarGroup $carGroup)
		{
			//$do = null;
			//$lastDistance = 0;
			foreach($this->warehouse->DeliveryOrderList as $key => $curDO)
			{
				/*if($curDO->Delivered)
					continue;
				$d = AbstractNode::getDistance($carGroup, $curDO);
				if($d <= $carGroup->MaxDistance && ($do === null || $d < $lastDistance))
				{
					$do = $curDO;
					$lastDistance = $d;
				}*/
                if(!$curDO->Delivered)return $curDO;
			}
			
			//return $do;
            //return $this->getWarehouse()->DeliveryOrderList[0];
		}
		
		private function getAvailableDO(CarGroup $car = NULL)
		{
			$doList = array();
			if($car === NULL) return $doList;
			//echo "<br />Car : " . $car->getCarID() . "(".$caSr->getLongitude().", w:". $car->Weight.", v:".$car->Volume.")<br />DO List : ";

			foreach($this->warehouse->DeliveryOrderList as $key => $val)
			{
				//echo "<br />DO : ".$val->ID."(".$val->getLongitude()."(".$val->Distance."), w:".$val->Weight.", v:". $val->Volume.")<br />";
				if($car->isDelivered($val) || $val->Delivered) continue;
				//echo "Delivered by ".$car->CarID."...<br />";
				$d = AbstractNode::getDistance($car, $val);
				$val->Distance = $d;
				if($d <= $car->MaxDistance)
					$doList[] = $val;
				/*else
					echo "too far : ".$d.":".$car->MaxDistance."<br />";*/
			}

			/*echo "<br />DO List : <br />";
			print_r($car);
			print_r($doList);
			echo "<br />";*/
			/*foreach($doList as $key => $val)
				echo $val->ID."(".$val->getLongitude()."(".$val->Distance."), w:".$val->Weight.", v:". $val->Volume.")".", ";
			echo "<br />Delivered : ";
			foreach($car->Path as $key => $val)
				echo $val->ID."(".$val->getLongitude()."(".$val->Distance."), w:".$val->Weight.", v:". $val->Volume.")".", ";
			echo "<br />";*/
			
			return $doList;
		}

        //private $carGroupIndex = 0;
        public function generate($db)
        {
            /*echo "<h1>".sizeof($this->getWarehouse()->CarList).":";*/
            $this->loadCarGroupTemplate($db);
            /*echo sizeof($this->getWarehouse()->CarList).":";
            $this->getWarehouse()->reset();
            echo sizeof($this->getWarehouse()->CarList).":";*/
            //$this->carGroupIndex = 0;
            $this->generateCarGroup();
            //echo sizeof($this->getWarehouse()->CarList)."</h1><br />";
        }

		private function generateCarGroup()
		{
            $idx = 0;
            foreach($this->carGroupTemplate as $carIndex => $carGroup)
			{
                $idx ++;
                if($carGroup->getLatitude() === 0 && $carGroup->getLongitude() === 0)
                {
                    $firstDO = $this->getWarehouse()->getDeliveryOrder(0);
                    $carGroup->setLatitude($firstDO->getLatitude());
                    $carGroup->setLongitude($firstDO->getLongitude());
                }

                $nearestDO = $this->getStarterNode($carGroup);
                if($nearestDO === null) continue;
				//TODO : DFS $newCarGroup = $this->calculate($carGroup, $nearestDO);

                //BFS
                //$ret = $carGroup->deliver($nearestDO);
                $carGroup->deliverDO($nearestDO);
                //echo $idx.":".$ret."<br />";
                //if(!$ret) continue;
                $this->bestFirstSearch($carGroup);
                $newCarGroup = $carGroup;

                if($newCarGroup === null) continue;
				foreach($newCarGroup->Path as $pathIndex => $path)
                {
                    $path->Delivered = true;
                    $path->Distance = AbstractNode::getDistance($carGroup, $path);
                }
                if(sizeof($newCarGroup->Path) > 0)
				    $this->warehouse->addCar($newCarGroup);

	            /*echo "<pre>";
	            print_r($newCarGroup);
				print_r($this->warehouse->DeliveryOrderList);
				echo "</pre>";*/
			}
            //TODO check whether there are still ungrouped DO, if there is then call generate again until there are no more ungrouped DO
            //echo "<br />Undelivered DO : <pre>";
            //print_r($this->warehouse->getUndeliveredDO());
            //echo "</pre>";
            /*if(sizeof($this->warehouse->getUndeliveredDO()) > 0 && $this->carGroupIndex <= 15)
            {
                $this->carGroupIndex ++;
                $this->generateCarGroup();
            }*/
		}

        private function bestFirstSearch(CarGroup $carGroup)
        {
            $curDO = null;
            //Search the best
            foreach($this->getAvailableDO($carGroup) as $key => $do)
            {
                if($carGroup->isValid($do))
                {
                    if($curDO === NULL || intval($curDO->Volume) < intval($do->Volume))
                        $curDO = $do;
                    /*else
                        echo "TAIX : " . (is_null($curDO)?"NULL":$curDO->Volume) . " < " . $do->Volume . " : " . ($curDO === NULL) . " || " . (intval($curDO->Volume) < intval($do->Volume)) . "<br />";*/
                }
            }

            if($curDO === NULL) return;
            $carGroup->deliverDO($curDO);
            $this->bestFirstSearch($carGroup);
        }
		
		private function calculate(CarGroup $car, DeliveryOrder $do = NULL)
		{
			if($do === NULL) return $car;
			//echo "<br />Car(".$car->getCarID().") : " . $car->Weight . ", " . $car->Volume . " to " . $do->Weight . ", " . $do->Volume . "<br />";
            $ret = $car->deliver($do);

			if(!$ret) return null;
			//echo $do->ID . " : " . $do->Weight . ", " . $do->Volume . " = " . $car->Weight . ", " . $car->Volume . " ---> " . ($ret ? "TRUE" : "FALSE") . "<br />";
			
			$curCar = NULL;
			foreach($this->getAvailableDO($car) as $key => $val)
			{
				//echo "X : ".$val->ID;
			    $newCar = $this->calculate($car->cloneObject(), $val);
                if($newCar === NULL) continue;
                if($curCar === NULL)
                {
                	$curCar = $newCar;
				}
                else
                {
                    if(/*$newCar->Weight > $curCar->Weight || */$newCar->Volume > $curCar->Volume)
                  	{
                  		unset($curCar);
                  		$curCar = $newCar;
					}
                }
			}
			if($curCar === null)
				$curCar = $car;
			
			unset($car);
            return $curCar;
        }
	}

?>